package ai2;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

public class InferenceEngine {

	private KnowledgeBase kb;
	private WorkingMemory wm;
	private ExplanationEngine ee;
	
	// create a blank InferenceEngine
	public InferenceEngine() {
		kb = new KnowledgeBase();
		wm = new WorkingMemory();
		System.out.println("IE ** created");
	}
	
	public InferenceEngine(String fileName) {
		kb = new KnowledgeBase(fileName);
		wm = new WorkingMemory();
	}
	
	// create an InferenceEngine with a filename containing the list of rules
	// and an ArrayList of facts
	public InferenceEngine(String fileName, ArrayList<String> facts) {
		System.out.println("IE ** created");
		System.out.println("IE ** rules filename: " + fileName);
		System.out.println("IE ** provided a list of facts" + "\n");
		// create a new KnowledgeBase and populate it from the specified file
		System.out.println("IE -- creating a new KnowledgeBase" + "\n");
		kb = new KnowledgeBase(fileName);
		// create a new WorkingMemory
		System.out.println("IE -- creating a new WorkingMemory" + "\n");
		// iterate through all the facts so WM has a chance to process them
		wm = new WorkingMemory();
		for (int i = 0; i < facts.size(); i++) {
			wm.addFact(facts.get(i));
		}
		// create a new ExplanationEngine (not yet implemented)
		System.out.println("IE -- creating a new ExplanationEngine" + "\n");
		ee = new ExplanationEngine();
	}
	
	// print all the information contained in the InferenceEngine
	public void report() {
		// give a system report
		System.out.println("IE -- reporting:");
		// if the KnowledgeBase has been created, print it
		if (kb != null) {
			kb.printRules();
		}
		else {
			// otherwise report the KnowledgeBase has not yet been created
			System.out.println("IE -- no KB created yet");
		}
		// if the WorkingMemory has been created, print the contents of it
		if (wm != null) {
			wm.printFacts();
		}
		else {
			// otherwise report that WorkingMemory has not yet been created
			System.out.println("IE -- no WM created yet");
		}
		// if ExplanationEngine has been created, print the contents of it
		if (ee != null) {
			ee.report();
		}
		else {
			// otherwise report that WorkingMemory has not yet been created
			System.out.println("IE -- no EE created yet");
		}
	}
	// make deductions about what is in working memory via our rules
	public void analyze() {
		System.out.println("IE -- analyzing");
		// get the list of rules from the KnowledgeBase
		ArrayList<Rule> rules = kb.getRules();
		// until no rule produces a new assertion or identification is made
		boolean noNewRules = false;
		// a place to hold the name of the matching object
		String objectName = "";
		while (!noNewRules) {
			noNewRules = true;
			// for each rule
			for (int i = 0; i < rules.size(); i++) {
				// try to support each of the rule's antecedents by matching it to known facts
				Rule currentRule = rules.get(i);
				System.out.println("IE -- currentRule:\n" + currentRule);
				ArrayList<String> antecedents = currentRule.getAntecedents();
				int totalMatchingAntecedents = 0;
				for (int j = 0; j < antecedents.size(); j++) {
					String currentAntecedent = antecedents.get(j);
					System.out.println("IE -- currentAntecedent: " + currentAntecedent);
					int matchingFactLocation = wm.findMatchingFact(currentAntecedent);
					if (wm.findMatchingFact(currentAntecedent) != -1) {
						objectName = wm.getFact(matchingFactLocation).split(" ")[0];
						System.out.println("IE -- objectName: " + objectName);
						totalMatchingAntecedents++;
					}
					System.out.println("IE -- totalMatchingAntecedents: " + totalMatchingAntecedents);
				}
				// if all the rule's antecedents are supported, assert the consequent
				if (totalMatchingAntecedents == antecedents.size()) {
					System.out.println("IE -- all antecedents supported");
					ArrayList<String> assertions = currentRule.getAssertions();
					for (int k = 0; k < assertions.size(); k++) {
						String currentAssertion = assertions.get(k);
						System.out.println("IE -- currentAssertion: " + currentAssertion);
						// unless there is an identical assertion already
						String boundAssertion = currentAssertion.replace("?x", objectName);
						if (!wm.containsFact(boundAssertion)) {
							// the assertion isn't in the list of facts
							System.out.println("IE -- doesn't contain assertion, adding it to facts");
							System.out.println("IE -- boundAssertion: " + boundAssertion);
							wm.addFact(boundAssertion);
							noNewRules = false;
						}
						else {
							// this assertion already exists
							System.out.println("IE -- this assertion already exists, not adding it");
						}
					}
				}
			}
		}	
	}
	
	public boolean validateHypothesis(String hypothesis) {
		System.out.println("IE -- validating hypothesis");
		boolean correct = false;
		System.out.println("IE -- hypothesis: " + hypothesis);
		int firstSpace = hypothesis.indexOf(" ");
		// remove the name of the object from the hypothesis
		String boundHypothesis = "?x "+ hypothesis.substring(firstSpace + 1, hypothesis.length());
		System.out.println("IE -- bound hypothesis: " + boundHypothesis);
		// create a list to hold the user's hypothesis, as well as the generated ones
		ArrayList<String> hypotheses = new ArrayList<String>();
		// add the user's hypothesis to the list
		hypotheses.add(boundHypothesis);
		ArrayList<Rule> rules = kb.getRules();
		// until all hypotheses have been tried and none have
		// been supported or until the original hypothesis has been validated
		while ((hypotheses.size() > 0) && !correct) {
			// for each hypothesis
			for (int i = hypotheses.size() - 1; i >= 0; i--) {
				String currentHypothesis = hypotheses.get(i);
				System.out.println("IE -- currentHypothesis: " + currentHypothesis);
				// once we have extracted the currentHypothesis, remove it from the list
				hypotheses.remove(i);
				// for each rule whose consequent matches the current hypothesis
				for (int j = 0; j < rules.size(); j++) {
					Rule currentRule = rules.get(j);
					ArrayList<String> currentAssertions = currentRule.getAssertions();
					for (int k = 0; k < currentAssertions.size(); k++) {
						String currentAssertion = currentAssertions.get(k);
						System.out.println("IE -- currentAssertion: " + currentAssertion);
						if (currentAssertion.equals(currentHypothesis)) {
							System.out.println("MATCH: " + currentAssertion);
							ArrayList<String> currentAntecedents = currentRule.getAntecedents();
							for (int l = 0; l < currentAntecedents.size(); l++) {
								hypotheses.add(currentAntecedents.get(l));
								System.out.println("IE -- adding: " + currentAntecedents.get(l));
							}
						}
					}
				}
			}
		}
		return correct;
	}
	
	public boolean validateHypothesis2(String hypothesis) {
		System.out.println("IE -- validating hypothesis");
		System.out.println("IE -- hypothesis: " + hypothesis);
		int firstSpace = hypothesis.indexOf(" ");
		// remove the name of the object from the hypothesis
		String name = hypothesis.substring(0, firstSpace);
		System.out.println("IE -- name: " + name);
		String boundHypothesis = "?x "+ hypothesis.substring(firstSpace + 1, hypothesis.length());
		System.out.println("IE -- bound hypothesis: " + boundHypothesis);
		// create a list to hold the user's hypothesis, as well as the generated ones
		ArrayList<String> hypotheses = new ArrayList<String>();
		ArrayList<String> entailed = new ArrayList<String>();
		// add the user's hypothesis to the list
		hypotheses.add(boundHypothesis);
		ArrayList<Rule> rules = kb.getRules();
		
		while (!hypotheses.isEmpty()) {
			// grab the current hypothesis
			String currentHypothesis = hypotheses.remove(hypotheses.size() - 1);
			System.out.println("IE -- currentHypothesis: " + currentHypothesis);
			// entail the current hypothesis
			entailed.add(currentHypothesis);
			System.out.println("IE -- entailed: " + currentHypothesis);
			if (!wm.containsFact(currentHypothesis)) {
				System.out.println("IE -- currentHypothesis not in WM");
				ArrayList<String> temp = new ArrayList<String>();
				for (int i = 0; i < rules.size(); i++) {
					Rule currentRule = rules.get(i);
					ArrayList<String> currentAssertions = currentRule.getAssertions();
					for (int j = 0; j < currentAssertions.size(); j++) {
						String currentAssertion = currentAssertions.get(j);
						if (currentAssertion.equals(currentHypothesis)) {
							System.out.println("IE -- match! CH: " + currentHypothesis);
							System.out.println("IE -- match! CA: " + currentAssertion);
							ArrayList<String> currentAntecedents = currentRule.getAntecedents();
							
							for (int k = 0; k < currentAntecedents.size(); k++) {
								String currentAntecedent = currentAntecedents.get(k);
								String unboundAntecedent = currentAntecedent.replace("?x", name);
								temp.add(currentAntecedent);
								System.out.println("IE -- adding antecedent to temp: " + unboundAntecedent);
							}
						}
					}
				}
				if (temp.size() == 0) {
					System.out.println("temp size: 0");
					// return false;
				}
				else {
					for (int i = 0; i < temp.size(); i++) {
						String tempAntecedent = temp.get(i);
						if (!entailed.contains(tempAntecedent)) {
							System.out.println("IE -- adding tempAntecedent to hypotheses: " + tempAntecedent);
							hypotheses.add(temp.get(i));
						}
					}
				}
			}
		}
		System.out.println("about to return true");
		return true;
	}
	
	public boolean validateHypothesis3(String hypothesis) {
		// collect rules we never matched
		Set<String> neverMatched = new HashSet<String>();
		String originalHypothesis = hypothesis;
		System.out.println("IE -- validating hypothesis");
		System.out.println("IE -- hypothesis: " + hypothesis);
		int firstSpace = hypothesis.indexOf(" ");
		// remove the name of the object from the hypothesis
		String name = hypothesis.substring(0, firstSpace);
		System.out.println("IE -- name: " + name);
		String boundHypothesis = "?x "+ hypothesis.substring(firstSpace + 1, hypothesis.length());
		System.out.println("IE -- bound hypothesis: " + boundHypothesis);
		// create a list to hold the user's hypothesis, as well as the generated ones
		Stack<String> hypotheses = new Stack<String>();
		// add the user's hypothesis to the list
		hypotheses.push(boundHypothesis);
		ArrayList<Rule> rules = kb.getRules();
		
		int count = 0;
		
		tooLong:
			while (!hypotheses.isEmpty()) {
			String currentHypothesis = hypotheses.peek();
			System.out.println("IE -- currentHypothises: " + currentHypothesis);
			
			for (int i = 0; i < rules.size(); i++) {
				Rule currentRule = rules.get(i);
				ArrayList<String> currentAssertions = currentRule.getAssertions();
				ArrayList<String> currentAntecedents = currentRule.getAntecedents();
				
				if (matchAssertions(currentHypothesis, currentAssertions)) {
					for (int j = 0; j < currentAntecedents.size(); j++) {
						System.out.println("IE -- pushing: " + currentAntecedents.get(j));
						hypotheses.push(currentAntecedents.get(j));
					}
				}
				else {
					neverMatched.add(currentHypothesis.replace("?x", name));
				}
				
				
				if (matchAntecedents(currentAntecedents, name)) {
					addFactsToWM(currentAssertions, name);
					for (int k = 0; k < currentAssertions.size(); k++) {
						String currentAssertion = currentAssertions.get(k).replace("?x", name);
						if (neverMatched.contains(currentAssertion)) {
							neverMatched.remove(currentAssertion);
						}
					}
					//hypotheses.pop();
				}
				else {
					for (int z = 0; z < currentAssertions.size(); z++) {
						String currentAssertion = currentAssertions.get(z).replace("?x", name);
						neverMatched.add(currentAssertion);
					}
				}
				
				if (wm.containsFact(originalHypothesis)) {
					System.out.println(originalHypothesis + ": TRUE");
					System.out.println("count: " + count);
					return true;
				}
				if (count > 500) {
					break tooLong;
				}
				count++;
			}
		}
		// if we get this far, we never found our hypothesis.
		System.out.println("returning false!");
		System.out.println("neverMatched: ");
		System.out.println(neverMatched);
		System.out.println("WM:");
		wm.printFacts();
		
		
		
		return false;
	}
	
	public void validateHypothesis4(String hypothesis) {
		System.out.println("IE -- hyp4");
		System.out.println("IE -- hypothesis: " + hypothesis);
		// get the list of rules from the KnowledgeBase
		ArrayList<Rule> rules = kb.getRules();
		// until no rule produces a new assertion or identification is made
		boolean noNewRules = false;
		// a place to hold the name of the matching object
		String objectName = "";
		while (!noNewRules) {
			noNewRules = true;
			// for each rule
			for (int i = 0; i < rules.size(); i++) {
				// try to support each of the rule's antecedents by matching it to known facts
				Rule currentRule = rules.get(i);
				System.out.println("IE -- currentRule:\n" + currentRule);
				ArrayList<String> antecedents = currentRule.getAntecedents();
				int totalMatchingAntecedents = 0;
				for (int j = 0; j < antecedents.size(); j++) {
					String currentAntecedent = antecedents.get(j);
					System.out.println("IE -- currentAntecedent: " + currentAntecedent);
					int matchingFactLocation = wm.findMatchingFact(currentAntecedent);
					if (wm.findMatchingFact(currentAntecedent) != -1) {
						objectName = wm.getFact(matchingFactLocation).split(" ")[0];
						System.out.println("IE -- objectName: " + objectName);
						totalMatchingAntecedents++;
					}
					System.out.println("IE -- totalMatchingAntecedents: " + totalMatchingAntecedents);
				}
				// if all the rule's antecedents are supported, assert the consequent
				if (totalMatchingAntecedents == antecedents.size()) {
					System.out.println("IE -- all antecedents supported");
					ArrayList<String> assertions = currentRule.getAssertions();
					for (int k = 0; k < assertions.size(); k++) {
						String currentAssertion = assertions.get(k);
						System.out.println("IE -- currentAssertion: " + currentAssertion);
						// unless there is an identical assertion already
						String boundAssertion = currentAssertion.replace("?x", objectName);
						if (!wm.containsFact(boundAssertion)) {
							// the assertion isn't in the list of facts
							System.out.println("IE -- doesn't contain assertion, adding it to facts");
							System.out.println("IE -- boundAssertion: " + boundAssertion);
							wm.addFact(boundAssertion);
							noNewRules = false;
						}
						else {
							// this assertion already exists
							System.out.println("IE -- this assertion already exists, not adding it");
						}
					}
				}
				else {
					// not matched
					
				}
			}
		}	
	}
	
	
	
	
	
	private boolean matchAssertions(ArrayList<String> hypotheses, ArrayList<String> assertions) {
		boolean match = true;
		
		if (hypotheses.size() > assertions.size()) {
			return false;
		}
		
		
		for (int i = 0; i < hypotheses.size(); i++) {
			if (!hypotheses.get(i).equals(assertions.get(i))) {
				match = false;
			}
		}
		return match;
	}
	
	private boolean matchAssertions(String hypothesis, ArrayList<String> assertions) {
		System.out.println("IE -- matching: " + hypothesis);
		boolean match = true;
		
		for (int i = 0; i < assertions.size(); i++) {
			if (!hypothesis.equals(assertions.get(i))) {
				match = false;
			}
		}
		System.out.println("IE -- returning: " + match);
		return match;
	}
	
	private boolean matchAntecedents(ArrayList<String> antecedents, String name) {
		System.out.println("IE -- matching antecedents");
		boolean match = true;
		
		for (int i = 0; i < antecedents.size(); i++) {
			String currentAntecedent = antecedents.get(i).replace("?x", name);
			if (!wm.containsFact(currentAntecedent)) {
				match = false;
			}
		}
		System.out.println("IE -- returning: " + match);
		return match;
	}
	
	private void addFactsToWM(ArrayList<String> assertions, String name) {
		for (int i = 0; i < assertions.size(); i++) {
			String currentAssertion = assertions.get(i).replace("?x", name);
			System.out.println("IE -- adding to WM: " + currentAssertion);
			if (!wm.containsFact(currentAssertion)) {
				wm.addFact(currentAssertion);
			}
		}
	}
	
	public WorkingMemory getWM() {
		return wm;
	}
	
	public KnowledgeBase getKB() {
		return kb;
	}
	
}